<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="./js/ctx.js"></script>
</head>
<style>

    #toolbar {
        background: rgba(42, 42, 42, 0.8);
        padding: 4px;
        border-radius: 4px;
    }

    #toolbar input {
        vertical-align: middle;
        padding-top: 2px;
        padding-bottom: 2px;
    }

    #toolbar .header {
        font-weight: bold;
    }
</style>
<body>
<div id="cesiumContainer">
</div>
<div id="toolbar">
    <table>
        <tbody>
        <tr>
            <td>Rate</td>
            <td>
                <input type="range" min="0.0" max="100.0" step="1" data-bind="value: emissionRate, valueUpdate: 'input'">
                <input type="text" size="5" data-bind="value: emissionRate">
            </td>
        </tr>

        <tr>
            <td>Size</td>
            <td>
                <input type="range" min="2" max="60.0" step="1" data-bind="value: particleSize, valueUpdate: 'input'">
                <input type="text" size="5" data-bind="value: particleSize">
            </td>
        </tr>

        <tr>
            <td>Min Life</td>
            <td>
                <input type="range" min="0.1" max="30.0" step="1" data-bind="value: minimumParticleLife, valueUpdate: 'input'">
                <input type="text" size="5" data-bind="value: minimumParticleLife">
            </td>
        </tr>

        <tr>
            <td>Max Life</td>
            <td>
                <input type="range" min="0.1" max="30.0" step="1" data-bind="value: maximumParticleLife, valueUpdate: 'input'">
                <input type="text" size="5" data-bind="value: maximumParticleLife">
            </td>
        </tr>

        <tr>
            <td>Min Speed</td>
            <td>
                <input type="range" min="0.0" max="30.0" step="1" data-bind="value: minimumSpeed, valueUpdate: 'input'">
                <input type="text" size="5" data-bind="value: minimumSpeed">
            </td>
        </tr>

        <tr>
            <td>Max Speed</td>
            <td>
                <input type="range" min="0.0" max="30.0" step="1" data-bind="value: maximumSpeed, valueUpdate: 'input'">
                <input type="text" size="5" data-bind="value: maximumSpeed">
            </td>
        </tr>

        <tr>
            <td>Start Scale</td>
            <td>
                <input type="range" min="0.0" max="10.0" step="1" data-bind="value: startScale, valueUpdate: 'input'">
                <input type="text" size="5" data-bind="value: startScale">
            </td>
        </tr>

        <tr>
            <td>End Scale</td>
            <td>
                <input type="range" min="0.0" max="10.0" step="1" data-bind="value: endScale, valueUpdate: 'input'">
                <input type="text" size="5" data-bind="value: endScale">
            </td>
        </tr>

        <tr>
            <td>Gravity</td>
            <td>
                <input type="range" min="-20.0" max="20.0" step="1" data-bind="value: gravity, valueUpdate: 'input'">
                <input type="text" size="5" data-bind="value: gravity">
            </td>
        </tr>

        <tr>
            <td>Translation</td>
            <td>
                X <input type="text" size="5" data-bind="value: transX">
                Y <input type="text" size="5" data-bind="value: transY">
                Z <input type="text" size="5" data-bind="value: transZ">
            </td>
        </tr>

        <tr>
            <td>Rotation</td>
            <td>
                H <input type="text" size="5" data-bind="value: heading">
                P <input type="text" size="5" data-bind="value: pitch">
                R <input type="text" size="5" data-bind="value: roll">
            </td>
        </tr>

        </tbody>
    </table>
</div>
<script>
    var initCesium = new InitCesium();
    var viewer = initCesium.initViewer('cesiumContainer',{
        animation: true,
        timeline: true,
        shouldAnimate : true
    });

    //Set the random number seed for consistent results.
    Cesium.Math.setRandomNumberSeed(3);

    //Set bounds of our simulation time
    var start = Cesium.JulianDate.fromDate(new Date(2015, 2, 25, 16));
    var stop = Cesium.JulianDate.addSeconds(start, 360, new Cesium.JulianDate());

    //Make sure viewer is at the desired time.
    viewer.clock.startTime = start.clone();
    viewer.clock.stopTime = stop.clone();
    viewer.clock.currentTime = start.clone();
    viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; //Loop at the end
    viewer.clock.multiplier = 1;
    viewer.clock.shouldAnimate = true;

    //Set timeline to simulation bounds
    viewer.timeline.zoomTo(start, stop);

    var viewModel = {
        emissionRate : 5.0,
        gravity : 0.0,
        minimumParticleLife : 1.0,
        maximumParticleLife : 1.0,
        minimumSpeed : 1.0,
        maximumSpeed : 4.0,
        startScale : 1.0,
        endScale : 5.0,
        particleSize : 25.0,
        transX : 2.5,
        transY : 4.0,
        transZ : 1.0,
        heading : 0.0,
        pitch : 0.0,
        roll : 0.0,
        fly : true,
        spin : true,
        show : true
    };

    Cesium.knockout.track(viewModel);
    var toolbar = document.getElementById('toolbar');
    Cesium.knockout.applyBindings(viewModel, toolbar);

    var entityPosition = new Cesium.Cartesian3();
    var entityOrientation = new Cesium.Quaternion();
    var rotationMatrix = new Cesium.Matrix3();
    var modelMatrix = new Cesium.Matrix4();

    function computeModelMatrix(entity, time) {
        var position = Cesium.Property.getValueOrUndefined(entity.position, time, entityPosition);
        if (!Cesium.defined(position)) {
            return undefined;
        }
        var orientation = Cesium.Property.getValueOrUndefined(entity.orientation, time, entityOrientation);
        if (!Cesium.defined(orientation)) {
            modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(position, undefined, modelMatrix);
        } else {
            modelMatrix = Cesium.Matrix4.fromRotationTranslation(Cesium.Matrix3.fromQuaternion(orientation, rotationMatrix), position, modelMatrix);
        }
        return modelMatrix;
    }

    var emitterModelMatrix = new Cesium.Matrix4();
    var translation = new Cesium.Cartesian3();
    var rotation = new Cesium.Quaternion();
    var hpr = new Cesium.HeadingPitchRoll();
    var trs = new Cesium.TranslationRotationScale();

    function computeEmitterModelMatrix() {
        hpr = Cesium.HeadingPitchRoll.fromDegrees(viewModel.heading, viewModel.pitch, viewModel.roll, hpr);

        trs.translation = Cesium.Cartesian3.fromElements(viewModel.transX, viewModel.transY, viewModel.transZ, translation);
        trs.rotation = Cesium.Quaternion.fromHeadingPitchRoll(hpr, rotation);

        return Cesium.Matrix4.fromTranslationRotationScale(trs, emitterModelMatrix);
    }

    //Generate a random circular pattern with varying heights.
    function computeCirclularFlight(lon, lat, radius) {
        var property = new Cesium.SampledPositionProperty();
        for (var i = 0; i <= 360; i += 45) {
            var radians = Cesium.Math.toRadians(i);
            var time = Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate());
            var position = Cesium.Cartesian3.fromDegrees(lon + (radius * 1.5 * Math.cos(radians)), lat + (radius * Math.sin(radians)), Cesium.Math.nextRandomNumber() * 500 + 1750);
            property.addSample(time, position);
        }
        return property;
    }

    //Compute the entity position property.
    var circularPosition = computeCirclularFlight(-112.110693, 36.0994841, 0.03);
    var staticPosition = Cesium.Cartesian3.fromDegrees(-112.110693, 36.0994841, 1000);

    var entity = viewer.entities.add({

        //Set the entity availability to the same interval as the simulation time.
        availability : new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({
            start : start,
            stop : stop
        })]),

        //Load the Cesium plane model to represent the entity
        model : {
            uri : '../data/models/CesiumAir/Cesium_Air.gltf',
            minimumPixelSize : 64
        },

        position : staticPosition
    });
    viewer.trackedEntity = entity;

    var scene = viewer.scene;
    var particleSystem = scene.primitives.add(new Cesium.ParticleSystem({
        image : '../data/fire.png',

        startColor : Cesium.Color.RED.withAlpha(0.7),
        endColor : Cesium.Color.YELLOW.withAlpha(0.3),

        startScale : viewModel.startScale,
        endScale : viewModel.endScale,

        minimumParticleLife : viewModel.minimumParticleLife,
        maximumParticleLife : viewModel.maximumParticleLife,

        minimumSpeed : viewModel.minimumSpeed,
        maximumSpeed : viewModel.maximumSpeed,

        imageSize : new Cesium.Cartesian2(viewModel.particleSize, viewModel.particleSize),

        // Particles per second.
        emissionRate : viewModel.emissionRate,

        bursts : [
            // these burst will occasionally sync to create a multicolored effect
            new Cesium.ParticleBurst({time : 5.0, minimum : 10, maximum : 100}),
            new Cesium.ParticleBurst({time : 10.0, minimum : 50, maximum : 100}),
            new Cesium.ParticleBurst({time : 15.0, minimum : 200, maximum : 300})
        ],

        lifetime : 16.0,

        emitter : new Cesium.CircleEmitter(2.0),

        emitterModelMatrix : computeEmitterModelMatrix(),

        updateCallback : applyGravity
    }));

    var gravityScratch = new Cesium.Cartesian3();

    function applyGravity(p, dt) {
        // We need to compute a local up vector for each particle in geocentric space.
        var position = p.position;

        Cesium.Cartesian3.normalize(position, gravityScratch);
        Cesium.Cartesian3.multiplyByScalar(gravityScratch, viewModel.gravity * dt, gravityScratch);

        p.velocity = Cesium.Cartesian3.add(p.velocity, gravityScratch, p.velocity);
    }

    viewer.scene.preUpdate.addEventListener(function(scene, time) {
        particleSystem.modelMatrix = computeModelMatrix(entity, time);

        // Account for any changes to the emitter model matrix.
        particleSystem.emitterModelMatrix = computeEmitterModelMatrix();

        // Spin the emitter if enabled.
        if (viewModel.spin) {
            viewModel.heading += 1.0;
            viewModel.pitch += 1.0;
            viewModel.roll += 1.0;
        }
    });

    Cesium.knockout.getObservable(viewModel, 'emissionRate').subscribe(
            function(newValue) {
                particleSystem.emissionRate = parseFloat(newValue);
            }
    );

    Cesium.knockout.getObservable(viewModel, 'particleSize').subscribe(
            function(newValue) {
                var particleSize = parseFloat(newValue);
                particleSystem.minimumImageSize.x = particleSize;
                particleSystem.minimumImageSize.y = particleSize;
                particleSystem.maximumImageSize.x = particleSize;
                particleSystem.maximumImageSize.y = particleSize;
            }
    );

    Cesium.knockout.getObservable(viewModel, 'minimumParticleLife').subscribe(
            function(newValue) {
                particleSystem.minimumParticleLife = parseFloat(newValue);
            }
    );

    Cesium.knockout.getObservable(viewModel, 'maximumParticleLife').subscribe(
            function(newValue) {
                particleSystem.maximumParticleLife = parseFloat(newValue);
            }
    );

    Cesium.knockout.getObservable(viewModel, 'minimumSpeed').subscribe(
            function(newValue) {
                particleSystem.minimumSpeed = parseFloat(newValue);
            }
    );

    Cesium.knockout.getObservable(viewModel, 'maximumSpeed').subscribe(
            function(newValue) {
                particleSystem.maximumSpeed = parseFloat(newValue);
            }
    );

    Cesium.knockout.getObservable(viewModel, 'startScale').subscribe(
            function(newValue) {
                particleSystem.startScale = parseFloat(newValue);
            }
    );

    Cesium.knockout.getObservable(viewModel, 'endScale').subscribe(
            function(newValue) {
                particleSystem.endScale = parseFloat(newValue);
            }
    );

    Cesium.knockout.getObservable(viewModel, 'fly').subscribe(
            function(newValue) {
                if (newValue) {
                    entity.position = circularPosition;
                    //Automatically compute orientation based on position movement.
                    entity.orientation = new Cesium.VelocityOrientationProperty(circularPosition);
                }
                else {
                    entity.position = staticPosition;
                    entity.orientation = undefined;
                }
            }
    );

    Sandcastle.addToggleButton('Spin', viewModel.spin, function(checked) {
        viewModel.spin = checked;
    });

    Sandcastle.addToggleButton('Fly', viewModel.fly, function(checked) {
        viewModel.fly = checked;
    });

    Sandcastle.addToggleButton('Show', viewModel.show, function(checked) {
        entity.show = checked;
        particleSystem.show = checked;
    });

    var options = [{
        text : 'Circle',
        onselect : function() {
            particleSystem.emitter = new Cesium.CircleEmitter(2.0);
        }
    }, {
        text : 'Sphere',
        onselect : function() {
            particleSystem.emitter = new Cesium.SphereEmitter(2.5);
        }
    }, {
        text : 'Cone',
        onselect : function() {
            particleSystem.emitter = new Cesium.ConeEmitter(Cesium.Math.toRadians(45.0));
        }
    }, {
        text : 'Box',
        onselect : function() {
            particleSystem.emitter = new Cesium.BoxEmitter(new Cesium.Cartesian3(10.0, 10.0, 10.0));
        }
    }];

    Sandcastle.addToolbarMenu(options);

</script>
</body>

</html>